require 'iconv'

class TaskController < ApplicationController
  before_filter :login_required

  # select tasks with filters
  # * session[:selected_project]
  # * session[:selected_resp]
  # * tags
  def select_tasks(tags='')
    # find user's projects' tasks
    @projectid = (session[:selected_project] || 0).to_i
    @userid = (session[:selected_resp] || 0).to_i
    sel_task = (session[:selected_task] || 0).to_i
    user_projects = session['user'].project_ids.join(',')
    cond = "done='0'"
    if user_projects != ''
      cond += " and project_id in (#{user_projects})"
      cond += " and project_id='#{@projectid}'" if @projectid != 0
      cond += " and user_id='#{@userid}'"       if @userid != 0
      if tags != ''
        # tag display and normal display is different: task_id is not filtered meaning that tree structured is not respected
        @tasks = Task.find_tagged_with(tags, {:conditions=>cond, :order=>'`order`'})
      else
        @tasks = Task.find(:all, :conditions=>cond, :order=>"`order`") #  + " and task_id=#{sel_task}"
      end
    else      
      @tasks = []
    end
    # add user's tasks without a project_id (happens when we create a new task)
    if(@userid==0)
      Task.find(:all, :conditions => "(project_id='0' and user_id='#{session['user']['id']}')").each {|t| @tasks << t }
    else
      Task.find(:all, :conditions => "(project_id='0' and user_id='#{@userid}')").each {|t| @tasks << t }
    end
    # gives only the tags of the current displayed tasks
    @tags = @tasks.inject([]) {|tags,t| tags += t.tags}.uniq.sort_by{|t| t.name.downcase}
  end

  # Partial
  def display_tasks_done
    @projectid = (session[:selected_project] || 0).to_i
    @userid = (session[:selected_resp] || 0).to_i
    user_projects = session['user'].project_ids.join(',')
    cond = "done='1' and project_id in (#{user_projects})"
    cond += " and project_id='#{@projectid}'" if @projectid != 0
    cond += " and user_id='#{@userid}'" if @userid != 0
    @tasksdone = Task.find(:all, :conditions=>cond, :order=>"`done_on` desc")
    render(:partial => 'tasksdone')
  end

  def disp_sub
    id = params[:id]
    session[:selected_task] = id if id != nil
    select_tasks
    render(:partial=>'taskcontent', :locals=>{:by_tag=>nil})
  end

  def expand_task
    t = Task.find(params[:id])
    select_tasks
    render(:partial=>'task_c', :locals=>{:task_c=>t, :tasks=>@tasks, :expand=>true})
  end
  
  def filter_by_tag
    tags  =  params[:t]
    session[:selected_task] = 0
    select_tasks(tags)
    render(:partial=>'taskcontent', :locals=>{:by_tag=>(tags=='' ? nil:true)})
  end
  
  def edit
    t = Task.find(params[:id])
    render(:partial => 'task_edit_c', :locals=>{:task=>t, :user=>t.user, :project=>t.project})
  end

  def index
    id = params[:id]
    session[:selected_project] = id if id != nil
    select_tasks
    flash[:notice] = "You can drag tasks to reorder them" if @tasks.size > 0
    flash[:members] = get_project_resp(@projectid)
    @resp = find_responsibles
  end

  def find_responsibles(project_id=0)
    if(project_id!=0)
      begin; @resp = Project.find(project_id).users; rescue; @resp = []; end
    else
      @resp = []
      session['user'].projects(true).each { |p|
        p.users.each { |u| @resp << u}
      }
      @resp.uniq!
      @resp = @resp.sort_by {|u| u.name}
    end
  end

  def post
    begin
      Task.update(params[:task].keys, params[:task].values)
    rescue
    end
    if(params[:new] and params[:new][:title] != '')
      params[:new][:order] = '0' if params[:new][:order] == ''
      t = Task.new(params[:new])
      t.save
    end

    flash[:notice] = "Saved"
    redirect_to(:action => "index")
  end

  # not used right now
  def priorize
    t = Task.find(params[:id])
    raise "no task for this id" if t == nil
    hm = params[:howmuch].to_i
    if hm < 0 # up
      if t.order > 1
        t.order += hm
        t.save
      else
        tasks = Task.find_all("order >= #{t.order} and id != '#{t.id}' and done!='1'")
        tasks.each {|t|
          t.order -= hm
          t.save
        }
      end
    else hm > 0 # down
      t.order += hm
      t.save
    end
    flash[:notice] = 'task priorized'
    redirect_to(:action => 'index')
  end

  def esc_js(js)
   (js || '').gsub(/\r\n|\n|\r/, "\\n").gsub(/["']/) { |m| "\\#{m}" }
  end

  # Ajaxed
  def createnew
    t             = Task.new
    t.user_id     = session['user']['id']
    t.project_id  = (session[:selected_project] || 0).to_i
    t.order       = 0
    t.task_id     = (session[:selected_task] || 0).to_i # the new task is the sibling of displayed tasks
    t.save
    inner = esc_js(render_to_string(:partial => "task_edit", :locals=>{:task=>t}))
    render(:text=> "displaynew(#{t.id},'#{inner}')")
  end

  # Ajaxed
  def add_sub
    parent_id     = params[:id]
    t             = Task.new
    t.user_id     = session['user']['id']
    t.project_id  = (session[:selected_project] || 0).to_i
    t.order       = 0 # relative to sub tasks
    t.task_id     = parent_id
    t.save
    render(:partial => 'task_edit', :locals=>{:task=>t, :user=>t.user, :project=>t.project})
  end

  # Ajaxed
  def save
    t_id  = params[:id]
    t     = Task.find(t_id)
    render(:text=> "$('s#{t_id}').src = image_path('save.png');alert('Can not find task #{t_id}');") and return if t == nil

    #user
    uname = params['user']['name']
    u     = User.find_by_name(uname)
    render(:text=>"$('s#{t.id}').src='/images/save.png';alert('user #{uname} not found')") and return if u == nil

    #project
    pname = params['project']['name']
    projects_by_name = Project.find_all_by_name(pname)
    #p = Project.find_by_sql(["select p.* FROM projects p limit 1"])# p LEFT OUTER JOIN projects_users u ON u.project_id=p.id WHERE p.name= ?",pname])
    if projects_by_name == []
      if(params['C']=='1')
        p = Project.new(:name => pname)
        p.save
        p.users << session['user']
        projects_by_name = [] << p
        # if this is the first project to this user, set it by default so the new task will have it by default
        session[:selected_project] = p.id if session['user'].projects.size == 1
      else
        render(:text=>"create_project_confirm('#{t_id}');")
        return
      end
    end
    found = nil
    projects_by_name.each do |p|
      found = true and break if p.users.member?(u)
    end
    render(:text=>"$('s#{t.id}').src='/images/save.png';alert('This project already exists and you do not have permission to add tasks to it.');") and return if !found
    #t.update_attributes(params[:task])
    t.title      = params[:task][:title]
    t.priority   = params[:task][:priority]
    done_bak = t.done
    t.done       = params[:task][:done]
    if(t.done == 1)
      t.done_on  = Time.now if done_bak == 0 # so we do not change the done date if the task was already done
    else
      t.done_on  = nil
    end
    t.duedate    = params[:task][:duedate]
    t.user_id    = u.id
    t.project_id = p.id

    # tags
    t.taggings.destroy_all
    if t.title.include?(':')
      t.tag_list = t.title.split(':')[0].split(',').map {|tag| tag.strip.squeeze(" ")}.join(', ')
    end
    t.save

    select_tasks # so we have @tags

    tags      = esc_js(render_to_string(:partial=>"tag", :collection => @tags))
    parent = t.task ? t.task : t
	  txt = esc_js(render_to_string(:partial => 'task_c', :locals => {:task_c=>parent, :tasks=>@tasks, :expand=>true}))
    render(:text => "saved('#{parent.id}','#{txt}',#{t.done},#{t.done!=done_bak});display_tags('#{tags}');")
  end


  # Ajaxed
  def delete
    id = params[:id]
    t = Task.find(id)
    if t == nil or (t.project_id!=0 and not session['user'].projects.map {|p| p.id}.include?(t.project_id))
      render(:text=> '-1') # this task is assigned to a project but not to one of this user
      return
    end
    if Task.destroy(id)
      render(:text=>"#{id}")
    else
      render(:text=>'-2')
    end
  end

  def select_project_index
    session[:selected_project] = params[:id].to_i
    select_tasks
    redirect_to(:action => 'index')
  end

  # return a html string with the responsibles of this project
  def get_project_resp p
    return '' if(p == 0)
    p_resp = find_responsibles(p).map{|u| u.name }
    return "<div>Project members:<ul class=\"mylist\"><li>#{p_resp.join("</li><li>")}</li></ul></div>"
  end

  #Ajaxed
  def show_project_resp
    render(:text => get_project_resp(params[:id].to_i))
  end

  #Ajaxed
  def select_project
    @projectid = params[:id].to_i
    session[:selected_project] = @projectid
    session[:selected_task] = 0 # display all tasks
    select_tasks
    content = esc_js(render_to_string(:partial=>"taskcontent"))
    tags    = esc_js(render_to_string(:partial=>"tag", :collection => @tags))
    resp    = esc_js(get_project_resp(params[:id].to_i))
    render(:text => "display_tasks('#{content}');display_tags('#{tags}');$('members').update('#{resp}')")
  end

  def update_resp
    render(:partial => "respcontent")
  end

  #Ajaxed
  def select_resp
    session[:selected_resp] = params[:id]
    select_tasks # TODO: tags ?
    render(:partial=>"taskcontent")
  end

  def order
    i = 1
    params[:taskslist].each { |id|
      Task.update(id, :order => i)
      i = i+1
    }
    render(:nothing => true)
  end

  def create_project
    render(:layout => false)
  end

  def export_cvs
    select_tasks
    str = "Order,Project,Priority,Title,Responsible,Due date\n"
    @tasks.each { |t|
      da = t.duedate.to_s
      str += '"'+t.order.to_s+'","'+t.project.name.to_s+'","'+t.priority.to_s+'","'+t.title.to_s+'","'+t.user.name.to_s+'","'+ da +"\"\n"
    }
    response.headers['Content-Type'] = 'text/csv'
    response.headers['Content-Disposition'] = 'attachment; filename=export.csv'
    render(:text => str)
  end

  def export_freemind
    response.headers['Content-Type'] = 'application/mm'
    response.headers['Content-Disposition'] = 'attachment; filename=export.mm'
    str = "<map version=\"0.8.1\">\n<node ID=\"root\" TEXT=\"tasks\">\n"
    select_tasks
    id = 1
    make_tree_from_project.each { |tag,tasks|
      str += "\n<node ID=\"tag_#{id}\" TEXT=\"#{tag}\">\n"
      tid = 1
      tasks.each { |task|
        str += "<node ID=\"#{id}_#{tid}\" TEXT=\"#{task}\"/>\n"
        tid += 1
      }
      str += "</node>\n"
      id += 1
    }
    str += "</node>\n</map>\n"
    render(:text => str)
  end

  # attach this task to his sibling
  # TODO: c'est pas le bon moyen visuelement.....
  def demote
    list = params[:taskslist]
    id = params[:id]
    index = list.index(id)
    if index and index > 0
      t = Task.find(id)
      parent = Task.find(list[index-1])
      t.task_id = parent.id if parent
      t.save
    end  
    #render(:text=>"t=#{t.id}, parent=#{parent.id}")
    select_tasks
    render(:partial => "task", :object => @task, :collection => @tasks, :order=>"`order`")
  end

  #Ajaxed
  def sort_by_priority
    tags  =  params[:t]
    select_tasks(tags)
    # sort
    @tasks = @tasks.sort_by { |t|
      p = t.priority
      d = t.duedate
      p = -1 if(t.priority == nil)
      d =  Date.civil(2030,1,1) if(t.duedate == nil)
      [d, p, -t.star, t.order]
      }
    i = 1
    @tasks.each { |t|
      Task.update(t.id, :order => i)
      i = i+1
      }
    render(:partial=>'taskcontent')
  end

  #Ajaxed
  def star_it
    begin
      t = Task.find(params[:id])
      case t.star
      when 0
        t.update_attribute(:star,1)
      when 1
        t.update_attribute(:star,0)
      end
      render(:text=>"star(#{t.id},#{t.star})")
    rescue Exception => e
      render(:text=>"alert(\"#{e.message}\")")
    end
  end

  private

  def sanitize txt
    HTMLEntities::encode_entities(Iconv.new('US-ASCII//TRANSLIT', 'utf-8').iconv(txt))
  end
  
  # used by export_freemind
  def make_tree_from_project
    tags = Hash.new { |hash, key| hash[key] = Array.new}
    @tasks.each {|t|
      if(t.tags.size==0)
        tags['not tagged'] << sanitize(t.title)
      else
        t.tags.each { |tag|
          tags[tag] << sanitize(t.title)
        }
      end
    }
    tags
  end
  
end

